#ifndef STRUCTMESHBASE_HPP
#define STRUCTMESHBASE_HPP

#include "meshBase.hpp"

namespace hms {

/**
 * @brief Interface class for structured meshes with quadrilateral cells
 * 
 * @tparam MeshType
 * 
 * Structured meshes are all those with a regular topology, only quadrilateral 
 * cells and exactly four corners.
 */
template<typename MeshType>
class StructMeshBase : public MeshBase<MeshType>
{
public:
	using EdgeType = typename MeshTraits<MeshType>::EdgeType;
	using CellType = typename MeshTraits<MeshType>::CellType;
	using SizeType = typename MeshTraits<MeshType>::SizeType;

protected:
	Index m_blocksize {8};

public:
	/* additional functions that must be implemented by derived mesh types */
	
	/**
	 * @brief Returns the number of cells in topological x-direction.
	 * 
	 * @return Index 
	 */
	auto nCellsX() const -> Index;
	/**
	 * @brief Returns the number of cells in topological y-direction.
	 * 
	 * @return Index 
	 */
	auto nCellsY() const -> Index;

	/**
	 * @brief Returns the number of cells in topological x- and y-direction.
	 * 
	 * @return SizeType Can be const Array2i& or Array2i, depending on mesh type.
	 */
	auto size() const -> SizeType;

	/* don't hide the inherited function, but provide an overload */
	using MeshBase<MeshType>::cell;
	/**
	 * @brief Returns a cell object created from 2D topological indices,
	 * e.g. (0,0) corresponds to the bottom left cell, (1,0) to its right
	 * neighbour.
	 * 
	 * @return CellType 
	 */
	auto cell(Index, Index) const -> CellType;

	/* moving boundary patches between different structured mesh types is 
	 * allowed, due to identical indexing */
	auto moveBoundaryPatches() ->
		std::vector<BoundaryPatch>&&;
	
	template<typename OtherMeshType>
	void moveBoundaryPatchesTo( StructMeshBase<OtherMeshType>& );

	auto blocksize() const -> Index;
	void blocksize(Index);

protected:
	StructMeshBase();
	
	/* many properties are the same for all types of structured meshes, so
	 * they are implemented here */
	auto cellImp(Index) const -> CellType;
	auto edgeImp(Index) const -> EdgeType;

	auto nVerticesImp     () const -> Index;
	auto nEdgesImp        () const -> Index;
	auto nInnerEdgesImp   () const -> Index;
	auto nBoundaryEdgesImp() const -> Index;
	auto nCellsImp        () const -> Index;
	auto nDomainCellsImp  () const -> Index;
	auto nGhostCellsImp   () const -> Index;
	

	// friend void optimiseMeshType( MeshVariant& );
	
	/* checks edge indices intended for a boundary patch for conformity with
	 * the mesh type (i.e. continuous indices at the top or bottom boundary,
	 * or a constant stride for left and right boundaries) */
	void checkBoundaryPatch( const ArrayXi& ) const;

	friend MeshBase<MeshType>;

public:
	/* to prevent slicing */
	StructMeshBase<MeshType>& operator=( MeshType&& ) = delete;
	StructMeshBase<MeshType>& operator=( const MeshType& ) = delete;
};



template<typename MeshType>
template<typename OtherMeshType>
void StructMeshBase<MeshType>::moveBoundaryPatchesTo(
	StructMeshBase<OtherMeshType>& other
){
	other.addBoundaryPatches( std::move(this->m_boundaryPatches) );
}


}

#endif